const express = require('express');
const router = express.Router();
const fs = require('fs').promises;
const path = require('path');
const { exec } = require('child_process');
const util = require('util');
const { verifyAdmin } = require('../middleware/auth');

const execPromise = util.promisify(exec);

/**
 * 安全路径验证，防止目录遍历攻击
 * @param {string} basePath 基础路径
 * @param {string} relativePath 相对路径
 * @returns {string} 完整路径
 */
const getSafePath = (basePath, relativePath) => {
  // 确保相对路径不包含 '../' 等试图访问上级目录的内容
  const normalizedPath = path.normalize(relativePath).replace(/^(\.\.(\/|\\|$))+/, '');
  return path.join(basePath, normalizedPath);
};

/**
 * @route   GET /api/file/list
 * @desc    列出目录内容
 * @access  私有
 * @query   {string} path - 相对路径
 */
router.get('/list', async (req, res) => {
  try {
    const requestPath = req.query.path || '/';
    const basePath = '/'; // 可以根据需要设置根目录
    const fullPath = getSafePath(basePath, requestPath);
    
    const stats = await fs.stat(fullPath);
    
    if (!stats.isDirectory()) {
      return res.status(400).json({ error: '指定的路径不是目录' });
    }
    
    const dirents = await fs.readdir(fullPath, { withFileTypes: true });
    
    const items = await Promise.all(dirents.map(async (dirent) => {
      const itemPath = path.join(fullPath, dirent.name);
      const stats = await fs.stat(itemPath);
      
      return {
        name: dirent.name,
        path: path.join(requestPath, dirent.name),
        isDirectory: dirent.isDirectory(),
        size: stats.size,
        mode: stats.mode.toString(8).slice(-3), // 将模式转换为八进制权限表示
        modifiedTime: stats.mtime.getTime(),
        accessTime: stats.atime.getTime(),
        uid: stats.uid,
        gid: stats.gid
      };
    }));
    
    // 按类型排序，目录在前，文件在后
    items.sort((a, b) => {
      if (a.isDirectory && !b.isDirectory) return -1;
      if (!a.isDirectory && b.isDirectory) return 1;
      return a.name.localeCompare(b.name);
    });
    
    res.json({
      path: requestPath,
      items
    });
  } catch (error) {
    res.status(500).json({ error: `访问目录失败: ${error.message}` });
  }
});

/**
 * @route   GET /api/file/content
 * @desc    获取文件内容
 * @access  私有
 * @query   {string} path - 文件路径
 */
router.get('/content', async (req, res) => {
  try {
    const filePath = req.query.path;
    if (!filePath) {
      return res.status(400).json({ error: '未提供文件路径' });
    }
    
    const basePath = '/';
    const fullPath = getSafePath(basePath, filePath);
    
    const stats = await fs.stat(fullPath);
    
    if (stats.isDirectory()) {
      return res.status(400).json({ error: '指定的路径是目录' });
    }
    
    // 检查文件大小，限制大文件的读取
    if (stats.size > 10 * 1024 * 1024) { // 10MB限制
      return res.status(400).json({ error: '文件过大，无法读取' });
    }
    
    const content = await fs.readFile(fullPath, 'utf8');
    
    res.json({
      path: filePath,
      content,
      size: stats.size,
      modifiedTime: stats.mtime.getTime()
    });
  } catch (error) {
    res.status(500).json({ error: `读取文件失败: ${error.message}` });
  }
});

/**
 * @route   POST /api/file/save
 * @desc    保存文件内容
 * @access  私有
 */
router.post('/save', async (req, res) => {
  try {
    const { path: filePath, content } = req.body;
    
    if (!filePath || content === undefined) {
      return res.status(400).json({ error: '未提供文件路径或内容' });
    }
    
    const basePath = '/';
    const fullPath = getSafePath(basePath, filePath);
    
    // 检查文件是否存在
    try {
      const stats = await fs.stat(fullPath);
      if (stats.isDirectory()) {
        return res.status(400).json({ error: '无法写入，指定的路径是目录' });
      }
    } catch (error) {
      // 文件不存在，会在后面创建
    }
    
    await fs.writeFile(fullPath, content, 'utf8');
    
    res.json({ message: '文件保存成功' });
  } catch (error) {
    res.status(500).json({ error: `保存文件失败: ${error.message}` });
  }
});

/**
 * @route   POST /api/file/create
 * @desc    创建文件或目录
 * @access  私有
 */
router.post('/create', async (req, res) => {
  try {
    const { path: itemPath, isDirectory } = req.body;
    
    if (!itemPath) {
      return res.status(400).json({ error: '未提供路径' });
    }
    
    const basePath = '/';
    const fullPath = getSafePath(basePath, itemPath);
    
    // 检查是否已存在
    try {
      await fs.stat(fullPath);
      return res.status(400).json({ error: '文件或目录已存在' });
    } catch (error) {
      // 文件不存在，继续创建
    }
    
    if (isDirectory) {
      await fs.mkdir(fullPath, { recursive: true });
      res.json({ message: '目录创建成功' });
    } else {
      await fs.writeFile(fullPath, '', 'utf8');
      res.json({ message: '文件创建成功' });
    }
  } catch (error) {
    res.status(500).json({ error: `创建失败: ${error.message}` });
  }
});

/**
 * @route   POST /api/file/delete
 * @desc    删除文件或目录
 * @access  私有
 */
router.post('/delete', async (req, res) => {
  try {
    const { path: itemPath } = req.body;
    
    if (!itemPath) {
      return res.status(400).json({ error: '未提供路径' });
    }
    
    const basePath = '/';
    const fullPath = getSafePath(basePath, itemPath);
    
    // 禁止删除根目录
    if (fullPath === '/' || fullPath === basePath) {
      return res.status(403).json({ error: '无法删除根目录' });
    }
    
    const stats = await fs.stat(fullPath);
    
    if (stats.isDirectory()) {
      await fs.rmdir(fullPath, { recursive: true });
      res.json({ message: '目录删除成功' });
    } else {
      await fs.unlink(fullPath);
      res.json({ message: '文件删除成功' });
    }
  } catch (error) {
    res.status(500).json({ error: `删除失败: ${error.message}` });
  }
});

/**
 * @route   POST /api/file/rename
 * @desc    重命名文件或目录
 * @access  私有
 */
router.post('/rename', async (req, res) => {
  try {
    const { oldPath, newPath } = req.body;
    
    if (!oldPath || !newPath) {
      return res.status(400).json({ error: '未提供原路径或新路径' });
    }
    
    const basePath = '/';
    const fullOldPath = getSafePath(basePath, oldPath);
    const fullNewPath = getSafePath(basePath, newPath);
    
    // 检查是否存在
    try {
      await fs.stat(fullNewPath);
      return res.status(400).json({ error: '目标文件或目录已存在' });
    } catch (error) {
      // 目标不存在，可以重命名
    }
    
    await fs.rename(fullOldPath, fullNewPath);
    
    res.json({ message: '重命名成功' });
  } catch (error) {
    res.status(500).json({ error: `重命名失败: ${error.message}` });
  }
});

/**
 * @route   POST /api/file/chmod
 * @desc    修改文件或目录权限
 * @access  私有 (仅管理员)
 */
router.post('/chmod', verifyAdmin, async (req, res) => {
  try {
    const { path: itemPath, mode, recursive } = req.body;
    
    if (!itemPath || !mode) {
      return res.status(400).json({ error: '未提供路径或权限模式' });
    }
    
    const basePath = '/';
    const fullPath = getSafePath(basePath, itemPath);
    
    // 验证权限模式
    if (!/^[0-7]{3}$/.test(mode)) {
      return res.status(400).json({ error: '无效的权限模式' });
    }
    
    let command = `chmod ${mode} "${fullPath}"`;
    if (recursive) {
      command = `chmod -R ${mode} "${fullPath}"`;
    }
    
    await execPromise(command);
    
    res.json({ message: '权限修改成功' });
  } catch (error) {
    res.status(500).json({ error: `修改权限失败: ${error.message}` });
  }
});

/**
 * @route   POST /api/file/search
 * @desc    搜索文件
 * @access  私有
 */
router.post('/search', async (req, res) => {
  try {
    const { path: searchPath, pattern, type } = req.body;
    
    if (!searchPath || !pattern) {
      return res.status(400).json({ error: '未提供搜索路径或模式' });
    }
    
    const basePath = '/';
    const fullPath = getSafePath(basePath, searchPath);
    
    let command;
    if (type === 'content') {
      // 搜索文件内容
      command = `grep -r "${pattern}" "${fullPath}" --include="*" | head -100`;
    } else {
      // 搜索文件名
      command = `find "${fullPath}" -name "*${pattern}*" | head -100`;
    }
    
    const { stdout } = await execPromise(command);
    const results = stdout.split('\n').filter(Boolean);
    
    res.json({
      count: results.length,
      results
    });
  } catch (error) {
    res.status(500).json({ error: `搜索失败: ${error.message}` });
  }
});

/**
 * @route   POST /api/file/compress
 * @desc    压缩文件或目录
 * @access  私有
 */
router.post('/compress', async (req, res) => {
  try {
    const { path: itemPath, target, type } = req.body;
    
    if (!itemPath || !target) {
      return res.status(400).json({ error: '未提供源路径或目标文件路径' });
    }
    
    const basePath = '/';
    const fullPath = getSafePath(basePath, itemPath);
    const fullTarget = getSafePath(basePath, target);
    
    // 确保源路径存在
    await fs.stat(fullPath);
    
    let command;
    if (type === 'zip') {
      command = `zip -r "${fullTarget}" "${fullPath}"`;
    } else if (type === 'tar') {
      command = `tar -czf "${fullTarget}" "${fullPath}"`;
    } else {
      return res.status(400).json({ error: '不支持的压缩类型' });
    }
    
    await execPromise(command);
    
    res.json({ message: '压缩成功', target });
  } catch (error) {
    res.status(500).json({ error: `压缩失败: ${error.message}` });
  }
});

/**
 * @route   POST /api/file/extract
 * @desc    解压文件
 * @access  私有
 */
router.post('/extract', async (req, res) => {
  try {
    const { path: archivePath, target } = req.body;
    
    if (!archivePath || !target) {
      return res.status(400).json({ error: '未提供归档文件路径或目标目录' });
    }
    
    const basePath = '/';
    const fullPath = getSafePath(basePath, archivePath);
    const fullTarget = getSafePath(basePath, target);
    
    // 确保源文件存在
    await fs.stat(fullPath);
    
    // 确保目标目录存在
    try {
      await fs.stat(fullTarget);
    } catch (error) {
      await fs.mkdir(fullTarget, { recursive: true });
    }
    
    let command;
    if (archivePath.endsWith('.zip')) {
      command = `unzip "${fullPath}" -d "${fullTarget}"`;
    } else if (archivePath.endsWith('.tar.gz') || archivePath.endsWith('.tgz')) {
      command = `tar -xzf "${fullPath}" -C "${fullTarget}"`;
    } else if (archivePath.endsWith('.tar')) {
      command = `tar -xf "${fullPath}" -C "${fullTarget}"`;
    } else {
      return res.status(400).json({ error: '不支持的归档格式' });
    }
    
    await execPromise(command);
    
    res.json({ message: '解压成功', target });
  } catch (error) {
    res.status(500).json({ error: `解压失败: ${error.message}` });
  }
});

module.exports = router;